home *** CD-ROM | disk | FTP | other *** search
/ GEGA 010 / GEGA010.iso / Mods / Doom 3 / BloodyScreme / bloodymess.pk4 / script / ai_monster_base.script next >
Text File  |  2004-08-04  |  45KB  |  2,089 lines

  1. /***********************************************************************
  2.  
  3. ai_monster_base.script
  4.  
  5. base class for all monsters.  implements common behavior.
  6.  
  7. ***********************************************************************/
  8.  
  9. //
  10. // attack flags
  11. //
  12. #define ATTACK_DODGE_LEFT    1
  13. #define ATTACK_DODGE_RIGHT    2
  14. #define ATTACK_COMBAT_NODE    4
  15. #define ATTACK_MELEE        8
  16. #define ATTACK_LEAP            16
  17. #define ATTACK_MISSILE        32
  18. #define ATTACK_SPECIAL1        64
  19. #define ATTACK_SPECIAL2        128
  20. #define ATTACK_SPECIAL3        256
  21. #define ATTACK_SPECIAL4        512
  22.  
  23.  
  24. #define AI_NOT_ACTIVATED    0
  25. #define AI_CHASING_ENEMY    1
  26. #define AI_LOST                2
  27. #define AI_PATH_FOLLOWING    3
  28. #define AI_ATTACK_NODE        4
  29.  
  30. /***********************************************************************
  31.  
  32.     base class for monsters
  33.  
  34. ***********************************************************************/
  35.  
  36. object monster_base : ai {
  37.     // common state variables
  38.     float        run_distance;            // distance to trigger running when chasing enemy
  39.     float        walk_turn;                // max turn amount allowed when running
  40.     boolean        run;
  41.     boolean        ambush;
  42.     boolean        ignoreEnemies;            // used to disable enemy checks during attack_path
  43.     boolean        stay_on_attackpath;        // used to disable enemy checks during attack_path
  44.     boolean        idle_sight_fov;    
  45.     float        customBlendOut;
  46.     entity        current_path;
  47.     entity        next_path;
  48.     boolean        resurrect;
  49.     boolean        ignore_lostcombat;
  50.     boolean        blocked;
  51.     boolean        ignore_sight;
  52.     
  53.     // common animation functions
  54.     void        Torso_CustomCycle();
  55.     void        Torso_CustomAnim();
  56.     void        Torso_Sight();
  57.     void        Torso_Death();
  58.     void        Legs_Idle();
  59.     void        Legs_Death();
  60.  
  61.     // attack checks
  62.     // these functions are meant to be implemented by subclasses.
  63.     // by default, they do nothing.
  64.     float        check_attacks();
  65.     void        do_attack( float attack_flags );
  66.     boolean        checkTurretAttack();                // tests if monster can still do a turret attack from current location
  67.  
  68.     // common functions
  69.     void        init();
  70.     void        state_Begin();
  71.     void        monster_begin();
  72.     void        archvile_minion();
  73.     boolean        can_resurrect();
  74.     void        monster_resurrect( entity enemy );
  75.     void        wait_for_enemy();
  76.     void        state_Killed();
  77.     void        state_Dead();
  78.     void        wake_on_enemy();
  79.     void        wake_on_trigger();
  80.     void        walk_on_trigger();
  81.     void        wake_on_attackcone();
  82.     void        sight_enemy();
  83.     void        enemy_dead();
  84.     void        state_Combat();
  85.     void        state_LostCombat();
  86.     void        state_Spawner();
  87.     void        monster_base_lost_combat();
  88.     boolean        check_blocked();
  89.     boolean        combat_chase();
  90.     void        combat_turret_node( entity node );
  91.     void        combat_attack_cone( entity node );
  92.     void        combat_ainode( entity node );
  93.     void        combat_lost();
  94.     void        combat_wander();
  95.     void        playCustomCycle( string animname, float blendTime );
  96.     void        playCustomAnim( string animname, float blendIn, float blendOut );
  97.     void        checkNodeAnim( entity node, string prefix, string anim );
  98.     void        trigger_wakeup_targets();
  99.     boolean        checkForEnemy( float use_fov );
  100.     void         state_FollowAlternatePath();
  101.     void         follow_alternate_path1();
  102.     void         follow_alternate_path2();
  103.     void         follow_alternate_path3();
  104.  
  105.     // path following
  106.     void         idle_followPathEntities( entity pathnode );
  107.     void         path_corner();
  108.     void         path_anim();
  109.     void         path_cycleanim();
  110.     void         path_turn();
  111.     void         path_wait();
  112.     void         path_waitfortrigger();
  113.     void         path_hide();
  114.     void         path_show();
  115.     void        path_attack();
  116. };
  117.  
  118. /***********************************************************************
  119.  
  120.     Torso animation control
  121.  
  122. ***********************************************************************/
  123.  
  124. void monster_base::Torso_CustomCycle() {
  125.     eachFrame {
  126.         ;
  127.     }
  128. }
  129.  
  130. void monster_base::Torso_CustomAnim() {
  131.     while( !animDone( ANIMCHANNEL_TORSO, customBlendOut ) ) {
  132.         waitFrame();
  133.     }
  134.     
  135.     finishAction( "customAnim" );
  136.     animState( ANIMCHANNEL_TORSO, "Torso_Idle", customBlendOut );
  137. }
  138.  
  139. void monster_base::Torso_Sight() {
  140.     string animname;
  141.     float blendFrames;
  142.     
  143.     if ( !getIntKey( "walk_on_sight" ) ) {
  144.         overrideAnim( ANIMCHANNEL_LEGS );
  145.     }
  146.  
  147.     animname = self.getKey( "on_activate" );
  148.     if ( self.getKey( "on_activate_blend" ) ) {
  149.         blendFrames = self.getIntKey( "on_activate_blend" );
  150.     } else {
  151.         blendFrames = 4;
  152.     }
  153.  
  154.     if ( animname != "" ) {
  155.         if ( hasAnim( ANIMCHANNEL_TORSO, animname ) ) {
  156.             playAnim( ANIMCHANNEL_TORSO, animname );
  157.         
  158.             while( !animDone( ANIMCHANNEL_TORSO, blendFrames ) ) {
  159.                 waitFrame();
  160.             }
  161.         }
  162.     }
  163.     
  164.     finishAction( "sight" );
  165.     animState( ANIMCHANNEL_TORSO, "Torso_Idle", blendFrames );
  166. }
  167.  
  168. void monster_base::Torso_Death() {
  169.     finishAction( "dead" );
  170. }
  171.  
  172. void monster_base::Legs_Idle() {
  173.     // implemented by subclasses
  174. }
  175.  
  176. void monster_base::Legs_Death() {
  177. }
  178.  
  179. /***********************************************************************
  180.  
  181.     AI
  182.  
  183. ***********************************************************************/
  184.  
  185. /*
  186. =====================
  187. monster_base::init
  188. =====================
  189. */
  190. void monster_base::init() {
  191.     ignoreEnemies        = false;
  192.     run_distance        = 0;
  193.     walk_turn            = 360;
  194.     ambush                = getIntKey( "ambush" );
  195.     ignore_lostcombat    = getIntKey( "ignore_lostcombat" );
  196.     stay_on_attackpath    = getIntKey( "stay_on_attackpath" );
  197.     idle_sight_fov        = true;
  198.     run                    = false;
  199. }
  200.  
  201. /*
  202. =====================
  203. monster_base::state_Begin
  204.  
  205. Initial state for monsters.  Called after spawn and when monster is resurrected.
  206. =====================
  207. */
  208. void monster_base::state_Begin() {
  209. }
  210.  
  211. /*
  212. =====================
  213. monster_base::do_attack
  214.  
  215. Performs an attack based on which bits are set on the attack_flags.
  216.  
  217. Implemented by subclasses
  218. =====================
  219. */
  220. void monster_base::do_attack( float attack_flags ) {
  221. }
  222.  
  223. /*
  224. =====================
  225. monster_base::check_attacks
  226.  
  227. Returns flags stating which attacks can be performed this frame.
  228.  
  229. Implemented by subclasses
  230. =====================
  231. */
  232. float monster_base::check_attacks() {
  233.     return 0;
  234. }
  235.  
  236. /*
  237. =====================
  238. monster_base::playCustomCycle
  239. =====================
  240. */
  241. void monster_base::playCustomCycle( string animname, float blendTime ) {
  242.     animState( ANIMCHANNEL_TORSO, "Torso_CustomCycle", blendTime );
  243.     overrideAnim( ANIMCHANNEL_LEGS );
  244.     playCycle( ANIMCHANNEL_TORSO, animname );
  245. }
  246.  
  247. /*
  248. =====================
  249. monster_base::playCustomAnim
  250. =====================
  251. */
  252. void monster_base::playCustomAnim( string animname, float blendIn, float blendOut ) {
  253.     customBlendOut = blendOut;
  254.     animState( ANIMCHANNEL_TORSO, "Torso_CustomAnim", blendIn );
  255.     overrideAnim( ANIMCHANNEL_LEGS );
  256.     playAnim( ANIMCHANNEL_TORSO, animname );
  257. }
  258.  
  259. /*
  260. =====================
  261. monster_base::trigger_wakeup_targets
  262. =====================
  263. */
  264. void monster_base::trigger_wakeup_targets() {
  265.     string key;
  266.     string name;
  267.     entity ent;
  268.  
  269.     key = getNextKey( "wakeup_target", "" );
  270.     while( key != "" ) {
  271.         name = getKey( key );
  272.         ent = sys.getEntity( name );
  273.         if ( !ent ) {
  274.             sys.warning( "Unknown wakeup_target '" + name + "' on entity '" + getName() + "'" );
  275.         } else {
  276.             sys.trigger( ent );
  277.         }
  278.         key = getNextKey( "wakeup_target", key );
  279.     }
  280. }
  281.  
  282. /*
  283. =====================
  284. monster_base::checkForEnemy
  285. =====================
  286. */
  287. boolean    monster_base::checkForEnemy( float use_fov ) {
  288.     entity enemy;
  289.     vector size;
  290.     float dist;
  291.  
  292.     if ( sys.influenceActive() ) {
  293.         return false;
  294.     }
  295.  
  296.     if ( AI_PAIN ) {
  297.         // get out of ambush mode when shot
  298.         ambush = false;
  299.     }
  300.  
  301.     if ( ignoreEnemies ) {
  302.         // while we're following paths, we only respond to enemies on pain, or when close enough to them
  303.         if ( stay_on_attackpath ) {
  304.             // don't exit attack_path when close to enemy
  305.             return false;
  306.         }
  307.  
  308.         enemy = getEnemy();
  309.         if ( !enemy ) {
  310.             enemy = findEnemy( false );
  311.         }
  312.     
  313.         if ( !enemy ) {
  314.             return false;
  315.         }
  316.  
  317.         size = getSize();
  318.         dist = ( size_x * 1.414 ) + 16;  // diagonal distance plus 16 units
  319.         if ( enemyRange() > dist ) {
  320.             return false;
  321.         }
  322.     } else {
  323.         if ( getEnemy() ) {
  324.             // we were probably triggered (which sets our enemy)
  325.             return true;
  326.         }
  327.  
  328.         if ( !ignore_sight ) {
  329.             enemy = findEnemy( use_fov );
  330.         }
  331.         
  332.         if ( !enemy ) {
  333.             if ( ambush ) {
  334.                 return false;
  335.             }
  336.  
  337.             enemy = heardSound( true );
  338.             if ( !enemy ) {
  339.                 return false;
  340.             }
  341.         }
  342.     }
  343.  
  344.     ignoreEnemies = false;
  345.  
  346.     // once we've woken up, get out of ambush mode
  347.     ambush = false;
  348.         
  349.     // don't use the fov for sight anymore
  350.     idle_sight_fov = false;
  351.  
  352.     setEnemy( enemy );
  353.     return true;
  354. }
  355.  
  356. /*
  357. =====================
  358. monster_base::state_Spawner
  359. =====================
  360. */
  361. void monster_base::state_Spawner() {
  362.     entity    ent;
  363.     float    triggerCount;
  364.     float    maxSpawn;
  365.     float    i;
  366.     string    name;
  367.  
  368.     maxSpawn = getIntKey( "spawner" );
  369.     name = getName();
  370.  
  371.     hide();
  372.  
  373.     triggerCount = 0;
  374.     AI_ACTIVATED = false;
  375.     while( 1 ) {
  376.         if ( AI_ACTIVATED ) {
  377.             triggerCount++;
  378.             AI_ACTIVATED = false;
  379.         }
  380.  
  381.         if ( triggerCount ) {
  382.             if ( canBecomeSolid() ) {
  383.                 for( i = 0; i < maxSpawn; i++ ) {
  384.                     ent = sys.getEntity( name + i );
  385.                     if ( !ent ) {
  386.                         break;
  387.                     }
  388.                 }
  389.  
  390.                 if ( i < maxSpawn ) {
  391.                     triggerCount--;
  392.                     sys.copySpawnArgs( self );
  393.                     sys.setSpawnArg( "spawner", "0" );
  394.                     sys.setSpawnArg( "name", name + i );
  395.                     if ( getKey( "spawn_target" ) != "" ) {
  396.                         sys.setSpawnArg( "target", getKey( "spawn_target" ) );
  397.                     }
  398.                     ent = sys.spawn( getKey( "classname" ) );
  399.                     sys.trigger( ent );
  400.                 }
  401.             }
  402.         }
  403.         waitFrame();
  404.     }
  405. }
  406.  
  407. /*
  408. =====================
  409. monster_base::monster_begin
  410. =====================
  411. */
  412. void monster_base::monster_begin() {
  413.     float    teleportType;
  414.     string    triggerAnim;
  415.     float    waittime;
  416.     boolean start_active;
  417.     entity  path;
  418.     float    movetype;
  419.     entity    enemy;
  420.  
  421.     run    = false;
  422.     ignore_sight = getIntKey( "no_sight" );
  423.  
  424.     if ( !getIntKey( "ignore_flashlight" ) ) {
  425.         // allow waking up from the flashlight
  426.         wakeOnFlashlight( true );
  427.     }
  428.  
  429.     start_active = false;
  430.  
  431.     if ( resurrect ) {
  432.         teleportType = 4;
  433.         triggerAnim = "";
  434.         AI_ACTIVATED = true;
  435.     } else {
  436.         teleportType = getIntKey( "teleport" );
  437.         triggerAnim = getKey( "trigger_anim" );
  438.     }
  439.  
  440.     if ( getIntKey( "spawner" ) ) {
  441.         setState( "state_Spawner" );
  442.     }
  443.  
  444.     if ( triggerAnim != "" ) {
  445.         //
  446.         // hide until triggered and then play a special animation
  447.         //
  448.         checkAnim( ANIMCHANNEL_TORSO, triggerAnim );
  449.         hide();
  450.         waitUntil( AI_ACTIVATED );
  451.         waitUntil( canBecomeSolid() );
  452.  
  453.         // don't go dormant during trigger_anim anims since they
  454.         // may end up floating in air during no gravity anims.
  455.         setNeverDormant( true );
  456.  
  457.         show();
  458.         trigger_wakeup_targets();
  459.         playCustomAnim( triggerAnim, 0, 4 );
  460.         waitAction( "customAnim" );
  461.         setNeverDormant( getFloatKey( "neverdormant" ) );
  462.         locateEnemy();
  463.         start_active = true;
  464.     } else if ( teleportType > 0 ) {
  465.         //
  466.         // teleport in when triggered
  467.         //
  468.         hide();
  469.         waitUntil( AI_ACTIVATED );
  470.         waitUntil( canBecomeSolid() );
  471.         becomeSolid();
  472.         movetype = getMoveType();
  473.         setMoveType( MOVETYPE_STATIC );
  474.  
  475.         // don't go dormant during teleport anims since they
  476.         // may end up floating in air during no gravity anims.
  477.         setNeverDormant( true );
  478.  
  479.         trigger_wakeup_targets();
  480.         if ( teleportType == 1 ) {
  481.             startFx( getKey( "fx_teleport1" ) );
  482.             wait( 1.6 );
  483.         } else if ( teleportType == 2 ) {
  484.             startFx( getKey( "fx_teleport2" ) );
  485.             wait( 2.6 );
  486.         } else if ( teleportType == 3 ) {
  487.             startFx( getKey( "fx_teleport3" ) );
  488.             wait( 3.6 );
  489.         } else {
  490.             startFx( getKey( "fx_teleport" ) );
  491.             wait( 0.6 );
  492.         }
  493.         show();
  494.         playCustomAnim( "teleport", 0, 4 );
  495.         waitAction( "customAnim" );
  496.         setNeverDormant( getFloatKey( "neverdormant" ) );
  497.         locateEnemy();
  498.         setMoveType( movetype );
  499.         start_active = true;
  500.     } else if ( getIntKey( "hide" ) ) {
  501.         //
  502.         // hide until triggered
  503.         //
  504.         hide();
  505.         waitUntil( AI_ACTIVATED );
  506.         if ( ( getIntKey( "hide" ) == 1 ) || ambush ) {
  507.             AI_ACTIVATED = false;
  508.             clearEnemy();
  509.         }
  510.         waitUntil( canBecomeSolid() );
  511.         show();
  512.     }
  513.  
  514.     waittime = getFloatKey( "wait" );
  515.     if ( waittime > 0 ) {
  516.         sys.wait( waittime );
  517.     }
  518.  
  519.     enemy = getEntityKey( "enemy" );    
  520.     if ( enemy ) {
  521.         setEnemy( enemy );
  522.     }
  523.  
  524.     if ( !start_active ) {
  525.         if ( getIntKey( "wake_on_attackcone" ) ) {
  526.             wake_on_attackcone();
  527.         } else if ( getIntKey( "walk_on_trigger" ) ) {
  528.             walk_on_trigger();
  529.         } else if ( getIntKey( "trigger" ) ) {
  530.             wake_on_trigger();
  531.         } else {
  532.             wake_on_enemy();
  533.         }
  534.     } else if ( getIntKey( "attack_path" ) ) {
  535.         // follow a path and fight player at end
  536.         path = randomPath();
  537.         if ( path ) {
  538.             run = true;
  539.             ignoreEnemies = true;
  540.             idle_followPathEntities( path );
  541.             ignoreEnemies = false;
  542.         }
  543.     }
  544.  
  545.     // allow him to see after he's woken up
  546.     ignore_sight = false;
  547.  
  548.     // ignore the flashlight from now on
  549.     wakeOnFlashlight( false );
  550. }
  551.  
  552. /*
  553. =====================
  554. monster_base::archvile_minion
  555. =====================
  556. */
  557. void monster_base::archvile_minion() {
  558.     hide();
  559.     resurrect = true;
  560.     AI_DEAD = true;
  561. }
  562.  
  563. /*
  564. =====================
  565. monster_base::can_resurrect
  566. =====================
  567. */
  568. boolean monster_base::can_resurrect() {
  569.     if ( !AI_DEAD ) {
  570.         return false;
  571.     }
  572.  
  573.     if ( !isHidden() ) {
  574.         return false;
  575.     }
  576.  
  577.     if ( !canBecomeSolid() ) {
  578.         return false;
  579.     }
  580.     
  581.     return true;
  582. }
  583.  
  584. /*
  585. =====================
  586. monster_base::state_Resurrect
  587. =====================
  588. */
  589. void monster_base::state_Resurrect() {
  590.     float health;
  591.     vector ang;
  592.  
  593.     AI_DEAD = false;
  594.  
  595.     stopMove();
  596.     hide();
  597.     stopRagdoll();
  598.     restorePosition();
  599.  
  600.     waitUntil( canBecomeSolid() );
  601.  
  602.     health = getFloatKey( "health" );
  603.     setHealth( health );
  604.  
  605.     ang_y = getFloatKey( "angle" );
  606.     setAngles( ang );
  607.     turnTo( ang_y );
  608.  
  609.     clearBurn();
  610.  
  611.     allowDamage();
  612.  
  613.     setState( "state_Begin" );
  614. }
  615.  
  616. /*
  617. =====================
  618. monster_base::monster_resurrect
  619. =====================
  620. */
  621. void monster_base::monster_resurrect( entity enemy ) {
  622.     // mark them as not dead so we don't get resurrected twice this frame
  623.     AI_DEAD = false;
  624.     if ( enemy ) {
  625.         setEnemy( enemy );
  626.     } else {
  627.         setEnemy( $player1 );
  628.     }
  629.     setState( "state_Resurrect" );
  630. }
  631.  
  632. /*
  633. =====================
  634. monster_base::wait_for_enemy
  635. =====================
  636. */
  637. void monster_base::wait_for_enemy() {
  638.     // prevent an infinite loop when in notarget
  639.     AI_PAIN = false;
  640.  
  641.     stopMove();
  642.     
  643.     while( !AI_PAIN && !getEnemy() ) {
  644.         if ( checkForEnemy( idle_sight_fov ) ) {
  645.             break;
  646.         }
  647.         waitFrame();
  648.     }
  649. }
  650.  
  651. /*
  652. =====================
  653. monster_base::state_Killed
  654. =====================
  655. */
  656. void monster_base::state_Killed() {
  657.     stopMove();
  658.  
  659.     animState( ANIMCHANNEL_TORSO, "Torso_Death", 0 );
  660.     animState( ANIMCHANNEL_LEGS, "Legs_Death", 0 );
  661.  
  662.     waitAction( "dead" );
  663.     setState( "state_Dead" );
  664. }
  665.  
  666. /*
  667. =====================
  668. monster_base::state_Dead
  669. =====================
  670. */
  671. void monster_base::state_Dead() {
  672.  
  673.  
  674. //    float burnDelay = getFloatKey( "burnaway" );
  675. //    if ( burnDelay != 0 ) {
  676. //        preBurn();
  677. //        sys.wait( burnDelay );
  678. //        burn();
  679. //        startSound( "snd_burn", SND_CHANNEL_BODY, false );
  680. //    }
  681.  
  682.     sys.wait( 3 );
  683.     
  684.     if ( resurrect ) {
  685.         hide();
  686.         restorePosition();
  687.  
  688.         // wait until we're resurrected
  689.         waitUntil( 0 );
  690.     }
  691. //    remove();
  692. }
  693. /*
  694. =====================
  695. monster_base::sight_enemy
  696. =====================
  697. */
  698. void monster_base::sight_enemy() {
  699.     string animname;
  700.  
  701.     faceEnemy();
  702.     animname = self.getKey( "on_activate" );
  703.     if ( animname != "" ) {
  704.         // don't go dormant during on_activate anims since they
  705.         // may end up floating in air during no gravity anims.
  706.         setNeverDormant( true );
  707.         if ( getIntKey( "walk_on_sight" ) ) {
  708.             moveToEnemy();
  709.         }
  710.         animState( ANIMCHANNEL_TORSO, "Torso_Sight", 4 );
  711.         waitAction( "sight" );
  712.         setNeverDormant( getFloatKey( "neverdormant" ) );
  713.     }
  714. }
  715.  
  716. /*
  717. =====================
  718. monster_base::enemy_dead
  719. =====================
  720. */
  721. void monster_base::enemy_dead() {
  722.     AI_ENEMY_DEAD = false;
  723.     checkForEnemy( false );
  724.     if ( !getEnemy() ) {
  725.         waitFrame();  // avoid infinite loops
  726.         setState( "state_Idle" );
  727.     } else {
  728.         setState( "state_Combat" );
  729.     }
  730. }
  731.  
  732. /*
  733. =====================
  734. monster_base::walk_on_trigger
  735. =====================
  736. */
  737. void monster_base::walk_on_trigger() {
  738.     string animname;
  739.     entity path;
  740.  
  741.     allowMovement( false );
  742.  
  743.     // sit in our idle anim till we're activated
  744.     animname = self.getKey( "anim" );
  745.     playCustomCycle( animname, 4 );
  746.     waitUntil( AI_ACTIVATED || AI_PAIN );
  747.     
  748.     if ( AI_ACTIVATED ) {
  749.         clearEnemy();
  750.         AI_ACTIVATED = false;
  751.     }
  752.     
  753.     animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
  754.     allowMovement( true );
  755.     
  756.     // follow a path
  757.     path = randomPath();
  758.     if ( path ) {
  759.         idle_followPathEntities( path );
  760.     }
  761.     
  762.     if ( !getEnemy() && !AI_ACTIVATED && !AI_PAIN ) {
  763.         // sit in our idle anim till we're activated
  764.         allowMovement( false );
  765.         playCustomCycle( animname, 4 );
  766.         while( !AI_PAIN && !AI_ACTIVATED ) {
  767.             if ( checkForEnemy( true ) ) {
  768.                 break;
  769.             }
  770.             waitFrame();
  771.         }
  772.         allowMovement( true );
  773.         animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
  774.     }
  775.     
  776.     trigger_wakeup_targets();
  777.     sight_enemy();
  778. }
  779.  
  780. /*
  781. =====================
  782. monster_base::wake_on_trigger
  783. =====================
  784. */
  785. void monster_base::wake_on_trigger() {
  786.     string    animname;
  787.     entity path;
  788.  
  789.     if ( !getIntKey( "attack_path" ) ) {
  790.         path = randomPath();
  791.         if ( path ) {
  792.             idle_followPathEntities( path );
  793.         }
  794.     }
  795.  
  796.     if ( !getEnemy() && !AI_ACTIVATED && !AI_PAIN ) {
  797.         // sit in our idle anim till we're activated
  798.         allowMovement( false );
  799.         
  800.         animname = self.getKey( "anim" );
  801.         playCustomCycle( animname, 4 );
  802.         
  803.         waitUntil( AI_ACTIVATED || AI_PAIN );
  804.         
  805.         allowMovement( true );
  806.         
  807.         animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
  808.     }
  809.     
  810.     trigger_wakeup_targets();
  811.     if ( getIntKey( "attack_path" ) ) {
  812.         // follow a path and fight player at end
  813.         path = randomPath();
  814.         if ( path ) {
  815.             ignoreEnemies = true;
  816.             run = true;
  817.             idle_followPathEntities( path );
  818.             ignoreEnemies = false;
  819.         }
  820.     } else {
  821.         sight_enemy();
  822.     }
  823. }
  824.  
  825. /*
  826. =====================
  827. monster_base::wake_on_enemy
  828. =====================
  829. */
  830. void monster_base::wake_on_enemy() {
  831.     string animname;
  832.     entity path;
  833.  
  834.     if ( !getIntKey( "attack_path" ) ) {
  835.         path = randomPath();
  836.         if ( path ) {
  837.             idle_followPathEntities( path );
  838.         }
  839.     }
  840.  
  841.     if ( !getEnemy() && !AI_ACTIVATED && !AI_PAIN ) {
  842.         // sit in our idle anim till we're activated
  843.         allowMovement( false );
  844.         animname = self.getKey( "anim" );
  845.         playCustomCycle( animname, 4 );
  846.  
  847.         while( !AI_PAIN && !AI_ACTIVATED ) {
  848.             if ( checkForEnemy( true ) ) {
  849.                 break;
  850.             }
  851.             waitFrame();
  852.         }
  853.     }
  854.     
  855.     allowMovement( true );
  856.     animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
  857.     
  858.     trigger_wakeup_targets();
  859.  
  860.     if ( getIntKey( "attack_path" ) ) {
  861.         // follow a path and fight player at end
  862.         path = randomPath();
  863.         if ( path ) {
  864.             run = true;
  865.             ignoreEnemies = true;
  866.             idle_followPathEntities( path );
  867.             ignoreEnemies = false;
  868.         }
  869.     } else {
  870.         sight_enemy();
  871.     }
  872. }
  873.  
  874. /*
  875. =====================
  876. monster_base::wake_on_attackcone
  877. =====================
  878. */
  879. void monster_base::wake_on_attackcone() {
  880.     string animname;
  881.     entity path;
  882.     entity enemy;
  883.  
  884.     if ( !getIntKey( "attack_path" ) ) {
  885.         path = randomPath();
  886.         if ( path ) {
  887.             idle_followPathEntities( path );
  888.             run = path.getIntKey( "run" );
  889.             while( !AI_MOVE_DONE && !AI_ACTIVATED && !AI_PAIN ) {
  890.                 enemy = findEnemyInCombatNodes();
  891.                 if ( enemy ) {
  892.                     setEnemy( enemy );
  893.                     break;
  894.                 }
  895.                 waitFrame();
  896.             }
  897.         }
  898.     }
  899.  
  900.     if ( !getEnemy() && !AI_ACTIVATED && !AI_PAIN ) {
  901.         // sit in our idle anim till we're activated
  902.         allowMovement( false );
  903.         
  904.         animname = self.getKey( "anim" );
  905.         playCustomCycle( animname, 4 );
  906.         
  907.         while( !AI_ACTIVATED && !AI_PAIN ) {
  908.             enemy = findEnemyInCombatNodes();
  909.             if ( enemy ) {
  910.                 setEnemy( enemy );
  911.                 break;
  912.             }
  913.             waitFrame();
  914.         }
  915.                 
  916.         allowMovement( true );
  917.         
  918.         animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
  919.     }
  920.  
  921.     trigger_wakeup_targets();
  922.     
  923.     if ( getIntKey( "attack_path" ) ) {
  924.         // follow a path and fight player at end
  925.         path = randomPath();
  926.         if ( path ) {
  927.             run = true;
  928.             ignoreEnemies = true;
  929.             idle_followPathEntities( path );
  930.             ignoreEnemies = false;
  931.         }
  932.     } else {
  933.         sight_enemy();
  934.     }
  935. }
  936.  
  937. /*
  938. =====================
  939. monster_base::idle_followPathEntities
  940. =====================
  941. */
  942. void monster_base::idle_followPathEntities( entity pathnode ) {
  943.     string nodeaction;
  944.     string triggername;
  945.     entity triggerent;
  946.     
  947.     current_path = pathnode;
  948.     do {
  949.         next_path = current_path.randomPath();
  950.         nodeaction = current_path.getKey( "classname" );
  951.         if ( hasFunction( nodeaction ) ) {
  952.             callFunction( nodeaction );
  953.         } else {
  954.             sys.warning( "'" + getName() + "' encountered an unsupported path entity '" + nodeaction + "' on entity '" + current_path.getName() + "'\n" );
  955.             return;
  956.         }
  957.  
  958.         if ( checkForEnemy( true ) ) {
  959.             return;
  960.         }
  961.         
  962.         // trigger any entities the path had targeted
  963.         triggername = current_path.getKey( "trigger" );
  964.         if ( triggername != "" ) {
  965.             triggerent = sys.getEntity( triggername );
  966.             if ( triggerent ) {
  967.                 triggerent.activate( self );
  968.             }
  969.         }
  970.  
  971.         current_path = next_path;
  972.     } while( !( !current_path ) );
  973. }
  974.  
  975. /***********************************************************************
  976.  
  977.     path functions
  978.  
  979. ***********************************************************************/
  980.  
  981. /*
  982. =====================
  983. monster_base::path_corner
  984. =====================
  985. */
  986. void monster_base::path_corner() {
  987.     string customAnim;
  988.  
  989.     if ( current_path.getKey( "run" ) != "" ) {
  990.         run = current_path.getIntKey( "run" );
  991.     }
  992.     customAnim = current_path.getKey( "anim" );
  993.  
  994.     while( 1 ) {
  995.         if ( customAnim != "" ) {
  996.             playCustomCycle( customAnim, 4 );
  997.         }
  998.         moveToEntity( current_path );
  999.         waitFrame();
  1000.         while( !AI_MOVE_DONE ) {
  1001.             if ( sys.influenceActive() ) {
  1002.                 break;
  1003.             }
  1004.             if ( checkForEnemy( true ) ) {
  1005.                 break;
  1006.             }
  1007.             waitFrame();
  1008.         }
  1009.  
  1010.         if ( customAnim != "" ) {
  1011.             animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
  1012.         }
  1013.  
  1014.         if ( sys.influenceActive() ) {
  1015.             stopMove();
  1016.             while( sys.influenceActive() ) {
  1017.                 waitFrame();
  1018.             }
  1019.             continue;
  1020.         }
  1021.         break;
  1022.     }
  1023.  
  1024.     if ( AI_DEST_UNREACHABLE ) {
  1025.         // Can't reach
  1026.         sys.warning( "entity '" + getName() + "' couldn't reach path_corner '" + current_path.getName() + "'" );
  1027.         waitFrame();
  1028.     }
  1029. }
  1030.  
  1031. /*
  1032. =====================
  1033. monster_base::path_anim
  1034. =====================
  1035. */
  1036. void monster_base::path_anim() {
  1037.     string animname;
  1038.     float  ang;
  1039.     float  blend_in;
  1040.     float  blend_out;
  1041.  
  1042.     animname = current_path.getKey( "anim" );
  1043.     blend_in = current_path.getIntKey( "blend_in" );
  1044.     blend_out = current_path.getIntKey( "blend_out" );
  1045.  
  1046.     if ( current_path.getKey( "angle" ) != "" ) {
  1047.         ang = current_path.getFloatKey( "angle" );
  1048.         turnTo( ang );
  1049.         while( !facingIdeal() ) {
  1050.             if ( checkForEnemy( true ) ) {
  1051.                 return;
  1052.             }
  1053.             waitFrame();
  1054.         }
  1055.     }
  1056.  
  1057.     playCustomAnim( animname, blend_in, blend_out );
  1058.     while( !animDone( ANIMCHANNEL_TORSO, blend_out ) ) {
  1059.         if ( checkForEnemy( true ) ) {
  1060.             break;
  1061.         }
  1062.         waitFrame();
  1063.     }
  1064.     animState( ANIMCHANNEL_TORSO, "Torso_Idle", blend_out );
  1065. }
  1066.  
  1067. /*
  1068. =====================
  1069. monster_base::path_cycleanim
  1070. =====================
  1071. */
  1072. void monster_base::path_cycleanim() {
  1073.     string animname;
  1074.     vector ang;
  1075.     float  blend_in;
  1076.     float  blend_out;
  1077.     float  waittime;
  1078.  
  1079.     animname = current_path.getKey( "anim" );
  1080.     blend_in = current_path.getIntKey( "blend_in" );
  1081.     blend_out = current_path.getIntKey( "blend_out" );
  1082.     ang = current_path.getAngles();
  1083.     turnTo( ang_y );
  1084.     while( !facingIdeal() ) {
  1085.         if ( checkForEnemy( true ) ) {
  1086.             return;
  1087.         }
  1088.         waitFrame();
  1089.     }
  1090.     playCustomCycle( animname, blend_in );
  1091.  
  1092.     waittime = current_path.getFloatKey( "wait" );
  1093.     if ( waittime ) {
  1094.         waittime += sys.getTime();
  1095.         while( sys.getTime() < waittime ) {
  1096.             if ( checkForEnemy( true ) ) {
  1097.                 return;
  1098.             }
  1099.             waitFrame();
  1100.         }
  1101.     } else {
  1102.         AI_ACTIVATED = false;
  1103.         while( !AI_ACTIVATED ) {
  1104.             if ( checkForEnemy( true ) ) {
  1105.                 return;
  1106.             }
  1107.             waitFrame();
  1108.         }
  1109.     }
  1110.     animState( ANIMCHANNEL_TORSO, "Torso_Idle", blend_out );
  1111. }
  1112.  
  1113. /*
  1114. =====================
  1115. monster_base::path_turn
  1116. =====================
  1117. */
  1118. void monster_base::path_turn() {
  1119.     vector ang;
  1120.  
  1121.     ang = current_path.getAngles();
  1122.     turnTo( ang_y );
  1123.     while( !facingIdeal() ) {
  1124.         if ( checkForEnemy( true ) ) {
  1125.             return;
  1126.         }
  1127.         waitFrame();
  1128.     }
  1129. }
  1130.  
  1131. /*
  1132. =====================
  1133. monster_base::path_wait
  1134. =====================
  1135. */
  1136. void monster_base::path_wait() {
  1137.     float waittime;
  1138.  
  1139.     waittime = current_path.getFloatKey( "wait" );
  1140.     waittime += sys.getTime();
  1141.     while( sys.getTime() < waittime ) {
  1142.         if ( checkForEnemy( true ) ) {
  1143.             return;
  1144.         }
  1145.         waitFrame();
  1146.     }
  1147. }
  1148.  
  1149. /*
  1150. =====================
  1151. monster_base::path_waitfortrigger
  1152. =====================
  1153. */
  1154. void monster_base::path_waitfortrigger() {
  1155.     AI_ACTIVATED = false;
  1156.     while( !AI_ACTIVATED ) {
  1157.         if ( checkForEnemy( true ) ) {
  1158.             return;
  1159.         }
  1160.         waitFrame();
  1161.     }
  1162.     AI_ACTIVATED = false;
  1163. }
  1164.             
  1165. /*
  1166. =====================
  1167. monster_base::path_hide
  1168. =====================
  1169. */
  1170. void monster_base::path_hide() {
  1171.     hide();
  1172. }
  1173.  
  1174. /*
  1175. =====================
  1176. monster_base::path_show
  1177. =====================
  1178. */
  1179. void monster_base::path_show() {
  1180.     waitUntil( canBecomeSolid() );
  1181.     show();
  1182. }
  1183.  
  1184. /*
  1185. =====================
  1186. monster_base::path_attack
  1187. =====================
  1188. */
  1189. void monster_base::path_attack() {
  1190.     entity    enemy;
  1191.     float    delta;
  1192.     boolean do_run;
  1193.     float    range;
  1194.     float    attack_flags;
  1195.  
  1196.     enemy = current_path.getEntityKey( "enemy" );    
  1197.     if ( !enemy ) {
  1198.         return;
  1199.     }
  1200.  
  1201.     setEnemy( enemy );
  1202.     locateEnemy();
  1203.  
  1204.     AI_ACTIVATED = false;
  1205.     while( !AI_ACTIVATED && !( !enemy ) ) {
  1206.         stopMove();
  1207.         while( sys.influenceActive() ) {
  1208.             waitFrame();
  1209.         }
  1210.         if ( enemyRange() > run_distance ) {
  1211.             do_run = true;
  1212.         } else {
  1213.             do_run = false;
  1214.         }
  1215.  
  1216.         // set our enemy again in case we were shot by the player
  1217.         setEnemy( enemy );
  1218.         if ( AI_ENEMY_DEAD ) {
  1219.             break;
  1220.         }
  1221.         moveToEnemy();
  1222.         if ( AI_MOVE_DONE ) {
  1223.             locateEnemy();
  1224.             moveToEnemy();
  1225.             if ( AI_MOVE_DONE ) {
  1226.                 // prevent runaway loops if monster can't reach enemy
  1227.                 waitFrame();
  1228.             }
  1229.         }
  1230.         while( !AI_ACTIVATED && !AI_MOVE_DONE && !AI_DEST_UNREACHABLE && !( !enemy ) ) {
  1231.             // set our enemy again in case we were shot by the player
  1232.             setEnemy( enemy );
  1233.             if ( AI_ENEMY_DEAD ) {
  1234.                 break;
  1235.             }
  1236.  
  1237.             moveToEnemy();
  1238.  
  1239.             if ( AI_ENEMY_IN_FOV ) {
  1240.                 lookAtEnemy( 1 );
  1241.             }
  1242.  
  1243.             if ( sys.influenceActive() ) {
  1244.                 break;
  1245.             }
  1246.             attack_flags = check_attacks();
  1247.             if ( attack_flags ) {
  1248.                 do_attack( attack_flags );
  1249.             }
  1250.             
  1251.             range = enemyRange();
  1252.             if ( range > run_distance ) {
  1253.                 do_run = true;
  1254.             }
  1255.             
  1256.             delta = getTurnDelta();
  1257.             if ( ( delta > walk_turn ) || ( delta < -walk_turn ) ) {
  1258.                 run = false;
  1259.             } else {
  1260.                 run = do_run;
  1261.             }
  1262.  
  1263.             waitFrame();
  1264.         }
  1265.         stopMove();
  1266.     }
  1267. }
  1268.  
  1269. /***********************************************************************
  1270.  
  1271.     Combat
  1272.  
  1273. ***********************************************************************/
  1274.  
  1275. /*
  1276. =====================
  1277. monster_base::state_Combat
  1278. =====================
  1279. */
  1280. void monster_base::state_Combat() {
  1281.     float attack_flags;
  1282.  
  1283.     eachFrame {
  1284.         faceEnemy();
  1285.         if ( AI_ENEMY_IN_FOV ) {
  1286.             lookAtEnemy( 1 );
  1287.         }
  1288.  
  1289.         if ( sys.influenceActive() ) {
  1290.             waitFrame();
  1291.             continue;
  1292.         }
  1293.  
  1294.         if ( AI_ENEMY_DEAD ) {
  1295.             enemy_dead();
  1296.         }
  1297.  
  1298.         attack_flags = check_attacks();
  1299.         if ( attack_flags ) {
  1300.             do_attack( attack_flags );
  1301.             continue;
  1302.         }
  1303.  
  1304.         if ( !combat_chase() ) {
  1305.             locateEnemy();
  1306.             if ( !combat_chase() ) {
  1307.                 combat_lost();
  1308.             }
  1309.         }
  1310.  
  1311.         waitFrame();
  1312.     }
  1313. }
  1314.  
  1315. /*
  1316. =====================
  1317. monster_base::check_blocked
  1318.  
  1319. returns true when an attack was called, since the move command may be different from when entering the function.
  1320. =====================
  1321. */
  1322. boolean monster_base::check_blocked() {
  1323.     entity    obstacle;
  1324.     float    attack_flags;
  1325.     monster_base monster;
  1326.     float    endTime;
  1327.     boolean oldrun;
  1328.  
  1329.     oldrun = run;
  1330.     if ( AI_BLOCKED ) {
  1331.         //sys.print( sys.getTime() + " : " + getName() + " is stuck in place\n" );
  1332.         saveMove();
  1333.         run = true;
  1334.         wander();
  1335.         endTime = sys.getTime() + 2;
  1336.         while( sys.getTime() < endTime ) {
  1337.             attack_flags = check_attacks();
  1338.             if ( attack_flags ) {
  1339.                 restoreMove();
  1340.                 do_attack( attack_flags );
  1341.                 run = oldrun;
  1342.                 return true;
  1343.             }
  1344.             waitFrame();
  1345.         }
  1346.         restoreMove();
  1347.     } else if ( moveStatus() == MOVE_STATUS_BLOCKED_BY_OBJECT ) {
  1348.         float force = getFloatKey( "kick_force" );
  1349.         if ( !force ) {
  1350.             force = 60;
  1351.         }
  1352.         kickObstacles( getObstacle(), force );
  1353.     } else if ( moveStatus() > MOVE_STATUS_BLOCKED_BY_OBJECT ) {
  1354.         // just wait for the path to be clear
  1355.         obstacle = getObstacle();
  1356.         monster = obstacle;
  1357.         if ( monster ) {
  1358.             if ( monster.blocked ) {
  1359.                 run = oldrun;
  1360.                 return false;
  1361.             }
  1362.         }
  1363.         blocked = true;
  1364.         saveMove();
  1365.         while( moveStatus() > MOVE_STATUS_BLOCKED_BY_OBJECT ) {
  1366.             run = true;
  1367.             wander();
  1368.             endTime = sys.getTime() + 1;
  1369.             while( sys.getTime() < endTime ) {
  1370.                 if ( AI_MOVE_DONE ) {
  1371.                     faceEnemy();
  1372.                 }
  1373.                 attack_flags = check_attacks();
  1374.                 if ( attack_flags ) {
  1375.                     blocked = false;
  1376.                     restoreMove();
  1377.                     do_attack( attack_flags );
  1378.                     run = oldrun;
  1379.                     return true;
  1380.                 }
  1381.                 waitFrame();
  1382.             }
  1383.             restoreMove();
  1384.         }
  1385.         blocked = false;
  1386.     }
  1387.  
  1388.     run = oldrun;
  1389.     return false;
  1390. }
  1391.  
  1392. /*
  1393. =====================
  1394. monster_base::combat_chase
  1395. =====================
  1396. */
  1397. boolean monster_base::combat_chase() {
  1398.     float    delta;
  1399.     boolean do_run;
  1400.     float    range;
  1401.     float    attack_flags;
  1402.     
  1403.     if ( !AI_ENEMY_VISIBLE || ( enemyRange() > run_distance ) ) {
  1404.         do_run = true;
  1405.     } else {
  1406.         do_run = false;
  1407.     }
  1408.  
  1409.     moveToEnemy();
  1410.     if ( AI_MOVE_DONE ) {
  1411.         return false;
  1412.     }
  1413.  
  1414.     waitFrame();
  1415.     if ( AI_MOVE_DONE ) {
  1416.         attack_flags = check_attacks();
  1417.         if ( attack_flags ) {
  1418.             do_attack( attack_flags );
  1419.             return true;
  1420.         }
  1421.         return false;
  1422.     }
  1423.     while( !AI_MOVE_DONE && !AI_DEST_UNREACHABLE ) {
  1424.         if ( AI_ENEMY_DEAD ) {
  1425.             enemy_dead();
  1426.         }
  1427.  
  1428.         if ( sys.influenceActive() ) {
  1429.             return true;
  1430.         }
  1431.         
  1432.         if ( AI_ENEMY_IN_FOV ) {
  1433.             lookAtEnemy( 1 );
  1434.         }
  1435.  
  1436.         attack_flags = check_attacks();
  1437.         if ( attack_flags ) {
  1438.             do_attack( attack_flags );
  1439.             return true;
  1440.         }
  1441.  
  1442.         if ( check_blocked() ) {
  1443.             return true;
  1444.         }
  1445.  
  1446.         range = enemyRange();
  1447.         if ( !AI_ENEMY_VISIBLE || ( range > run_distance ) ) {
  1448.             do_run = true;
  1449.         }
  1450.         
  1451.         delta = getTurnDelta();
  1452.         if ( ( delta > walk_turn ) || ( delta < -walk_turn ) ) {
  1453.             run = false;
  1454.         } else {
  1455.             run = do_run;
  1456.         }
  1457.  
  1458.         waitFrame();
  1459.     }
  1460.  
  1461.     return true;
  1462. }
  1463.  
  1464. /*
  1465. =====================
  1466. monster_base::checkTurretAttack
  1467.  
  1468. tests if monster can still do a turret attack from current location
  1469. =====================
  1470. */
  1471. boolean monster_base::checkTurretAttack() {
  1472.     return canHitEnemy();
  1473. }
  1474.  
  1475. /*
  1476. =====================
  1477. monster_base::combat_turret_node
  1478. =====================
  1479. */
  1480. void monster_base::combat_turret_node( entity node ) {
  1481.     vector    pos;
  1482.     float    min_wait;
  1483.     float    max_wait;
  1484.     float    num_shots;
  1485.     float    wait_end;
  1486.     float    current_time;
  1487.     float    count;
  1488.     boolean    exit;
  1489.     boolean do_melee;
  1490.  
  1491.     min_wait = node.getFloatKey( "min_wait" );
  1492.     max_wait = node.getFloatKey( "max_wait" );
  1493.     num_shots = node.getFloatKey( "num_shots" );
  1494.  
  1495.     pos = node.getOrigin();
  1496.  
  1497.     exit = false;
  1498.     while( !AI_ENEMY_DEAD ) {
  1499.         if ( !node ) {
  1500.             // level designers sometimes remove the combat node, so check for it so the thread doesn't get killed
  1501.             break;
  1502.         }
  1503.         // run to the combat node
  1504.         allowMovement( true );
  1505.         run = true;
  1506.  
  1507.         moveToEntity( node );
  1508.         while( !AI_MOVE_DONE ) {
  1509.             if ( sys.influenceActive() ) {
  1510.                 stopMove();
  1511.                 while( sys.influenceActive() ) {
  1512.                     waitFrame();
  1513.                 }
  1514.                 moveToEntity( node );
  1515.             }
  1516.  
  1517.             if ( !node ) {
  1518.                 // level designers sometimes remove the combat node, so check for it so the thread doesn't get killed
  1519.                 break;
  1520.             }
  1521.  
  1522.             if ( AI_ENEMY_IN_FOV ) {
  1523.                 lookAtEnemy( 1 );
  1524.             }
  1525.             // exit out if we can't get there or we can do a melee attack
  1526.             do_melee = testMeleeAttack();
  1527.             if ( AI_DEST_UNREACHABLE || do_melee ) {
  1528.                 animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
  1529.                 return;
  1530.             }
  1531.             waitFrame();
  1532.         }
  1533.  
  1534.         if ( !AI_ENEMY_VISIBLE ) {
  1535.             waitFrame();
  1536.             continue;
  1537.         }
  1538.  
  1539.         if ( !checkTurretAttack() ) {
  1540.             waitFrame();
  1541.             continue;
  1542.         }
  1543.  
  1544.         if ( !node ) {
  1545.             // level designers sometimes remove the combat node, so check for it so the thread doesn't get killed
  1546.             break;
  1547.         }
  1548.  
  1549.         faceEnemy();
  1550.         allowMovement( false );
  1551.  
  1552.         // make sure he's precisely at the node
  1553.         slideTo( pos, 0.25 );
  1554.         waitUntil( AI_MOVE_DONE );
  1555.         faceEnemy();
  1556.     
  1557.         // do our attack
  1558.         allowMovement( false );
  1559.         
  1560.         count = int( sys.random( num_shots ) ) + 1;
  1561.         while( count > 0 ) {
  1562.             if ( sys.influenceActive() ) {
  1563.                 break;
  1564.             }
  1565.             if ( !node ) {
  1566.                 // level designers sometimes remove the combat node, so check for it so the thread doesn't get killed
  1567.                 exit = true;
  1568.                 break;
  1569.             }
  1570.             animState( ANIMCHANNEL_TORSO, "Torso_TurretAttack", 4 );
  1571.             while( inAnimState( ANIMCHANNEL_TORSO, "Torso_TurretAttack" ) ) {
  1572.                 if ( AI_ENEMY_IN_FOV ) {
  1573.                     lookAtEnemy( 1 );
  1574.                 }
  1575.                 waitFrame();
  1576.             }
  1577.             if ( testMeleeAttack() || AI_ENEMY_DEAD ) {
  1578.                 exit = true;
  1579.                 break;
  1580.             }
  1581.             count--;
  1582.         }
  1583.  
  1584.         if ( exit ) {
  1585.             break;
  1586.         }
  1587.         
  1588.         faceEnemy();
  1589.         current_time = sys.getTime();
  1590.         wait_end = current_time + min_wait + sys.random( max_wait - min_wait );
  1591.         while( sys.influenceActive() || ( sys.getTime() < wait_end ) ) {
  1592.             if ( AI_ENEMY_IN_FOV ) {
  1593.                 lookAtEnemy( 1 );
  1594.             }
  1595.             // exit out if we can do a melee attack or enemy is dead
  1596.             // level designers sometimes remove the combat node, so check for it so the thread doesn't get killed
  1597.             if ( testMeleeAttack() || AI_ENEMY_DEAD || !node ) {
  1598.                 exit = true;
  1599.                 break;
  1600.             }
  1601.             waitFrame();
  1602.         }
  1603.         
  1604.         if ( exit ) {
  1605.             break;
  1606.         }
  1607.     }
  1608.     
  1609.     if ( node ) {
  1610.         // we're done with the node, so get rid of it so we don't use it again
  1611.         node.remove();
  1612.     }
  1613.     
  1614.     allowMovement( true );
  1615.     animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
  1616. }
  1617.  
  1618. /*
  1619. =====================
  1620. monster_base::checkNodeAnim
  1621. =====================
  1622. */
  1623. void monster_base::checkNodeAnim( entity node, string prefix, string anim ) {
  1624.     if ( !hasAnim( ANIMCHANNEL_TORSO, prefix + "_" + anim ) ) {
  1625.         sys.error( "AI node '" + node.getName() + "' specifies missing anim '" + prefix + "_" + anim + "' on monster '" + getName() + "'" );
  1626.     }
  1627. }
  1628.  
  1629. /*
  1630. =====================
  1631. monster_base::combat_attack_cone
  1632. =====================
  1633. */
  1634. void monster_base::combat_attack_cone( entity node ) {
  1635.     vector    ang;
  1636.     vector    pos;
  1637.     float    min_wait;
  1638.     float    max_wait;
  1639.     float    num_shots;
  1640.     float    wait_end;
  1641.     float    current_time;
  1642.     float    count;
  1643.     boolean    exit;
  1644.     boolean    attack;
  1645.     boolean do_melee;
  1646.     boolean dont_wait;
  1647.     string    prefix;
  1648.  
  1649.     // run to the combat node
  1650.     run = true;
  1651.     moveToEntity( node );
  1652.     while( !AI_MOVE_DONE ) {
  1653.         if ( AI_ENEMY_IN_FOV ) {
  1654.             lookAtEnemy( 1 );
  1655.         }
  1656.         // exit out if we can't get there or we can do a melee attack
  1657.         do_melee = testMeleeAttack();
  1658.         if ( AI_DEST_UNREACHABLE || do_melee || !enemyInCombatCone( node, false ) || AI_ENEMY_DEAD || !node ) {
  1659.             animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
  1660.             return;
  1661.         }
  1662.         waitFrame();
  1663.     }
  1664.  
  1665.     if ( !node ) {
  1666.         // level designers sometimes remove the combat node, so check for it so the thread doesn't get killed
  1667.         animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
  1668.         return;
  1669.     }
  1670.  
  1671.     // set our anim prefix
  1672.     prefix = node.getKey( "anim" );
  1673.     if ( prefix == "" ) {
  1674.         sys.error( "Missing 'anim' key on entity '" + node.getName() + "'" );
  1675.     }
  1676.     setAnimPrefix( prefix );
  1677.     checkNodeAnim( node, prefix, "out" );
  1678.     checkNodeAnim( node, prefix, "fire" );
  1679.     checkNodeAnim( node, prefix, "in" );
  1680.     checkNodeAnim( node, prefix, "wait" );
  1681.     
  1682.     min_wait = node.getFloatKey( "min_wait" );
  1683.     max_wait = node.getFloatKey( "max_wait" );
  1684.     num_shots = node.getFloatKey( "num_shots" );
  1685.     dont_wait = getIntKey( "wake_on_attackcone" );
  1686.     if ( dont_wait ) {
  1687.         setKey( "wake_on_attackcone", "0" );
  1688.     }
  1689.  
  1690.     // face the direction the node points
  1691.     ang = node.getAngles();
  1692.     turnTo( ang_y );
  1693.     
  1694.     exit = false;
  1695.     attack = false;
  1696.     pos = node.getOrigin();
  1697.     while( !( !node ) && enemyInCombatCone( node, true ) ) {
  1698.         allowMovement( false );
  1699.  
  1700.         // play the wait animation
  1701.         playCustomCycle( "wait", 4 );
  1702.         
  1703.         // make sure he's precisely at the node
  1704.         slideTo( pos, 0.5 );
  1705.         current_time = sys.getTime();
  1706.         if ( dont_wait ) {
  1707.             dont_wait = false;
  1708.             wait_end = current_time + 0.5;
  1709.         } else {
  1710.             wait_end = current_time + min_wait + sys.random( max_wait - min_wait );
  1711.         }
  1712.         while( sys.influenceActive() || !AI_MOVE_DONE || !facingIdeal() || ( current_time < wait_end ) ) {
  1713.             if ( AI_ENEMY_IN_FOV ) {
  1714.                 lookAtEnemy( 1 );
  1715.             }
  1716.             // exit out if we can do a melee attack or enemy has left the combat cone
  1717.             if ( testMeleeAttack() ) {
  1718.                 exit = true;
  1719.                 break;
  1720.             }
  1721.             if ( !node || !enemyInCombatCone( node, false ) ) {
  1722.                 exit = true;
  1723.                 break;
  1724.             }
  1725.             waitFrame();
  1726.             current_time = sys.getTime();
  1727.         }
  1728.         
  1729.         if ( exit ) {
  1730.             break;
  1731.         }
  1732.  
  1733.         // do our attack
  1734.         allowMovement( true );
  1735.         playCustomAnim( "out", 4, 0 );
  1736.         while( !animDone( ANIMCHANNEL_TORSO, 0 ) ) {
  1737.             if ( AI_ENEMY_IN_FOV ) {
  1738.                 lookAtEnemy( 1 );
  1739.             }
  1740.             if ( testMeleeAttack() ) {
  1741.                 exit = true;
  1742.                 break;
  1743.             }
  1744.             if ( !node || !enemyInCombatCone( node, true ) ) {
  1745.                 exit = true;
  1746.                 break;
  1747.             }                
  1748.             waitFrame();
  1749.         }
  1750.         
  1751.         if ( exit ) {
  1752.             break;
  1753.         }
  1754.  
  1755.         AI_DAMAGE = false;
  1756.         allowMovement( false );
  1757.         count = int( sys.random( num_shots ) ) + 1;
  1758.         while( !AI_DAMAGE && ( count > 0 ) ) {
  1759.             if ( sys.influenceActive() ) {
  1760.                 break;
  1761.             }
  1762.             playCustomAnim( "fire", 0, 0 );
  1763.             while( !animDone( ANIMCHANNEL_TORSO, 0 ) ) {
  1764.                 if ( sys.influenceActive() ) {
  1765.                     break;
  1766.                 }
  1767.                 if ( AI_ENEMY_IN_FOV ) {
  1768.                     lookAtEnemy( 1 );
  1769.                 }
  1770.                 if ( testMeleeAttack() ) {
  1771.                     exit = true;
  1772.                     break;
  1773.                 }
  1774.                 if ( !node || !enemyInCombatCone( node, true ) ) {
  1775.                     exit = true;
  1776.                     break;
  1777.                 }                
  1778.                 waitFrame();
  1779.             }
  1780.             attack = true;
  1781.             
  1782.             if ( testMeleeAttack() ) {
  1783.                 exit = true;
  1784.                 break;
  1785.             }
  1786.             if ( !node || !enemyInCombatCone( node, true ) ) {
  1787.                 exit = true;
  1788.                 break;
  1789.             }                
  1790.             count--;
  1791.         }
  1792.         AI_DAMAGE = false;
  1793.         
  1794.         if ( exit ) {
  1795.             break;
  1796.         }
  1797.  
  1798.         allowMovement( true );
  1799.         playCustomAnim( "in", 0, 4 );
  1800.         while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
  1801.             if ( AI_ENEMY_IN_FOV ) {
  1802.                 lookAtEnemy( 1 );
  1803.             }
  1804.             if ( testMeleeAttack() ) {
  1805.                 exit = true;
  1806.                 break;
  1807.             }
  1808.             if ( !node || !enemyInCombatCone( node, true ) ) {
  1809.                 exit = true;
  1810.                 break;
  1811.             }                
  1812.             waitFrame();
  1813.         }
  1814.         
  1815.         if ( exit ) {
  1816.             break;
  1817.         }
  1818.     }
  1819.     
  1820.     if ( !( !node ) && attack ) {
  1821.         // mark the node as used in case it's single use
  1822.         node.markUsed();
  1823.     }
  1824.     
  1825.     allowMovement( true );
  1826.     setAnimPrefix( "" );
  1827.     animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
  1828.     animState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 );
  1829. }
  1830.  
  1831. /*
  1832. =====================
  1833. monster_base::combat_ainode
  1834. =====================
  1835. */
  1836. void monster_base::combat_ainode( entity node ) {
  1837.     if ( node.getKey( "classname" ) == "ai_attackcone_turret" ) {
  1838.         combat_turret_node( node );
  1839.     } else {
  1840.         combat_attack_cone( node );
  1841.     }
  1842. }
  1843.  
  1844. /*
  1845. =====================
  1846. monster_base::combat_lost
  1847. =====================
  1848. */
  1849. void monster_base::combat_lost() {
  1850.     if ( !ignore_lostcombat ) {
  1851.         setState( "state_LostCombat" );
  1852.     }
  1853. }
  1854.  
  1855. /*
  1856. =====================
  1857. monster_base::state_LostCombat
  1858. =====================
  1859. */
  1860. void monster_base::state_LostCombat() {
  1861.     monster_base_lost_combat();
  1862. }
  1863.  
  1864. /*
  1865. =====================
  1866. monster_base::monster_base_lost_combat
  1867. =====================
  1868. */
  1869. void monster_base::monster_base_lost_combat() {
  1870.     entity node;
  1871.     vector ang;
  1872.     float dist;
  1873.     float yaw;
  1874.     float attack_flags;
  1875.     entity possibleEnemy;
  1876.     float allow_attack;
  1877.     float lost_time;
  1878.  
  1879.     lost_time = sys.getTime() + sys.getFrameTime();
  1880.     allow_attack = sys.getTime() + 4;
  1881.     
  1882.     node = getClosestHiddenTarget( "ai_lostcombat" );
  1883.     if ( node ) {
  1884.         dist = distanceTo( node );
  1885.         if ( dist < 40 ) {
  1886.             // fixes infinite loops when close to lost combat node
  1887.             waitFrame();
  1888.         } else {
  1889.             run = true;
  1890.             moveToEntity( node );
  1891.             while( !AI_MOVE_DONE ) {
  1892.                 if ( sys.influenceActive() ) {
  1893.                     break;
  1894.                 }
  1895.                 possibleEnemy = heardSound( true );
  1896.                 if ( possibleEnemy ) {
  1897.                     if ( canReachEntity( possibleEnemy ) ) {
  1898.                         setEnemy( possibleEnemy );
  1899.                         break;
  1900.                     }
  1901.                 }
  1902.                 if ( canReachEnemy() ) {
  1903.                     if ( AI_ENEMY_IN_FOV || AI_PAIN ) {
  1904.                         break;
  1905.                     }
  1906.                 }
  1907.                 if ( check_blocked() ) {
  1908.                     break;
  1909.                 }
  1910.                 waitFrame();
  1911.  
  1912.                 // allow attacks when enemy is outside of fov
  1913.                 if ( sys.getTime() > allow_attack ) {
  1914.                     AI_ENEMY_IN_FOV = AI_ENEMY_VISIBLE;
  1915.                 }
  1916.                 attack_flags = check_attacks();
  1917.                 if ( attack_flags ) {
  1918.                     do_attack( attack_flags );
  1919.                     setState( "state_Combat" );
  1920.                 }
  1921.             }
  1922.             ang = node.getAngles();
  1923.             turnTo( ang_y );
  1924.             while( !AI_MOVE_DONE ) {
  1925.                 if ( canReachEnemy() ) {
  1926.                     if ( heardSound( true ) ) {
  1927.                         break;
  1928.                     }
  1929.                     if ( AI_ENEMY_IN_FOV || AI_PAIN ) {
  1930.                         break;
  1931.                     }
  1932.                 }
  1933.                 waitFrame();
  1934.             }
  1935.         }
  1936.     } else {
  1937.         run = true;
  1938.         moveToCover();
  1939.         if ( AI_DEST_UNREACHABLE ) {
  1940.             combat_wander();
  1941.         }
  1942.         
  1943.         // if we're not already in cover
  1944.         if ( !AI_MOVE_DONE ) {
  1945.             while( !AI_MOVE_DONE ) {
  1946.                 if ( sys.influenceActive() ) {
  1947.                     break;
  1948.                 }
  1949.                 // allow attacks when enemy is outside of fov
  1950.                 if ( sys.getTime() > allow_attack ) {
  1951.                     AI_ENEMY_IN_FOV = AI_ENEMY_VISIBLE;
  1952.                 }
  1953.                 attack_flags = check_attacks();
  1954.                 if ( attack_flags ) {
  1955.                     do_attack( attack_flags );
  1956.                     setState( "state_Combat" );
  1957.                 }
  1958.                 
  1959.                 possibleEnemy = heardSound( true );
  1960.                 if ( possibleEnemy ) {
  1961.                     if ( canReachEntity( possibleEnemy ) ) {
  1962.                         setEnemy( possibleEnemy );
  1963.                         break;
  1964.                     }
  1965.                 }
  1966.                 if ( canReachEnemy() ) {
  1967.                     if ( AI_ENEMY_IN_FOV || AI_PAIN ) {
  1968.                         break;
  1969.                     }
  1970.                 }
  1971.                 
  1972.                 if ( check_blocked() ) {
  1973.                     break;
  1974.                 }
  1975.                 waitFrame();
  1976.             }
  1977.  
  1978.             
  1979.             if ( !sys.influenceActive() ) {
  1980.                 if ( AI_ENEMY_VISIBLE ) {
  1981.                     faceEnemy();
  1982.                 } else if ( AI_MOVE_DONE ) {
  1983.                     // turn around to face the way we came
  1984.                     yaw = getCurrentYaw();
  1985.                     turnTo( yaw + 180 );
  1986.                 }
  1987.             }
  1988.         }
  1989.     }
  1990.  
  1991.     // wait at least 1 frame to avoid loops
  1992.     waitFrame();
  1993.  
  1994.     if ( lost_time >= sys.getTime() ) {
  1995.         combat_wander();
  1996.     }
  1997.  
  1998.     if ( AI_ENEMY_VISIBLE || sys.influenceActive() ) {
  1999.         setState( "state_Combat" );
  2000.     } else {
  2001.         clearEnemy();
  2002.         setState( "state_Idle" );
  2003.     }
  2004. }
  2005.  
  2006. /*
  2007. =====================
  2008. monster_base::combat_wander
  2009. =====================
  2010. */
  2011. void monster_base::combat_wander() {
  2012.     float endtime;
  2013.     float mintime;
  2014.  
  2015.     mintime = sys.getTime() + 0.2;
  2016.     endtime = sys.getTime() + 3;
  2017.     wander();
  2018.     while( sys.getTime() < endtime ) {
  2019.         if ( sys.influenceActive() ) {
  2020.             break;
  2021.         }
  2022.         if ( check_attacks() ) {
  2023.             break;
  2024.         }
  2025.         if ( sys.getTime() > mintime ) {
  2026.             if ( canReachEnemy() ) {
  2027.                 if ( heardSound( true ) ) {
  2028.                     break;
  2029.                 }
  2030.                 if ( AI_ENEMY_IN_FOV || AI_PAIN ) {
  2031.                     break;
  2032.                 }
  2033.             }
  2034.         }
  2035.  
  2036.         waitFrame();
  2037.     }
  2038.  
  2039.     // wait at least 1 frame to avoid loops
  2040.     waitFrame();
  2041.     setState( "state_Combat" );
  2042. }
  2043.  
  2044. /*
  2045. =====================
  2046. monster_base::state_FollowAlternatePath
  2047. =====================
  2048. */
  2049. void monster_base::state_FollowAlternatePath() {
  2050.     if ( inAnimState( ANIMCHANNEL_TORSO, "Torso_CustomCycle" ) || inAnimState( ANIMCHANNEL_TORSO, "Torso_CustomAnim" ) ) {
  2051.         animState( ANIMCHANNEL_TORSO, "Torso_Idle", 8 );
  2052.     }
  2053.  
  2054.     ignoreEnemies = true;
  2055.     idle_followPathEntities( current_path );
  2056.     ignoreEnemies = false;
  2057.     setState( "state_Idle" );
  2058. }
  2059.  
  2060. /*
  2061. =====================
  2062. monster_base::follow_alternate_path1
  2063. =====================
  2064. */
  2065. void monster_base::follow_alternate_path1() {
  2066.     current_path = getEntityKey( "alt_path1" );
  2067.     setState( "state_FollowAlternatePath" );
  2068. }
  2069.  
  2070. /*
  2071. =====================
  2072. monster_base::follow_alternate_path2
  2073. =====================
  2074. */
  2075. void monster_base::follow_alternate_path2() {
  2076.     current_path = getEntityKey( "alt_path2" );
  2077.     setState( "state_FollowAlternatePath" );
  2078. }
  2079.  
  2080. /*
  2081. =====================
  2082. monster_base::follow_alternate_path3
  2083. =====================
  2084. */
  2085. void monster_base::follow_alternate_path3() {
  2086.     current_path = getEntityKey( "alt_path2" );
  2087.     setState( "state_FollowAlternatePath" );
  2088. }
  2089.